package com.zhang.learn.io.nio.channel;

import org.junit.Test;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * @author zhanghaolian
 * @Description
 * 一、通道，通道本身不存储数据，需要配合缓冲区来使用
 * 二、通道的主要实现类
 *  java.nio.channels.Channel 接口
 *      |--FileChannel  本地的文件
 *      |--SocketChannel
 *      |--ServerSocketChannel
 *      |--DatagramChannel  udp的一些操作
 *
 * 三、获取通道
 *  1、java对支持通道的类提供了getChannel()方法
 *      本地io
 *      FileInputStream.getChannel();
 *      FileOutputStream.getChannel();
 *      RandomAccessFileInputStream.getChannel(); 等
 *
 *      网络io
 *      Socket.getChannel();
 *      ServerSocket.getChannel();
 *      Datagram.getChannel();
 *
 *  2、在JDK1.7中的NIO.2针对各个通道提供了一个静态方法:open();
 *  3、在JDK1.7中的NIO.2的Files工具类的:newByteChannel();方法
 *
 * 四、通道之间数据传输
 *  transferFrom(); 从另外一个通道中获取数据
 *  transferTo();   将当前通道的数据传送到另一个通道
 *
 * 五、分散(scatter)与聚集(gather)
 *  1、分散读取(Scattering Reads)
 *  2、聚集写入(Gathering Writes)
 *
 *
 * @ClassName ChannelTest
 * @date 2021/2/13 21:55
 */
public class ChannelTest {

    /**
     * 测试分散读取和聚集写入
     */
    @Test
    public void testScatterAndGather() throws IOException {
        RandomAccessFile file = new RandomAccessFile("application.yaml", "rw");

        // 获取通道
        final FileChannel channel = file.getChannel();

        // 分配指定大小的缓冲区
        ByteBuffer buffer1 = ByteBuffer.allocate(100);
        ByteBuffer buffer2 = ByteBuffer.allocate(1000);

        ByteBuffer[] buffers = new ByteBuffer[2];
        buffers[0] = buffer1;
        buffers[1] = buffer2;

        // 分散读取
        channel.read(buffers);

        List<String> list = new ArrayList<>();



    }

    /**
     * 测试通道间数据传送
     * transferFrom();
     * transferTo();
     */
    @Test
    public void testTransferDataFromChannel() {
        String readPath = ChannelTest.class.getResource("/").getPath().substring(1) + "application.yaml";
        String writePath = ChannelTest.class.getResource("/").getPath().substring(1) + "application4.yaml";
        try(
                FileChannel readChannel = FileChannel.open(Paths.get(readPath), StandardOpenOption.READ);
                FileChannel writeChannel = FileChannel.open(
                        Paths.get(writePath),
                        StandardOpenOption.READ,
                        StandardOpenOption.WRITE,
                        StandardOpenOption.CREATE)
        ) {
            //用transferTo();或者transferFrom();
            //readChannel.transferTo(0, readChannel.size(), writeChannel);
            writeChannel.transferFrom(readChannel, 1, readChannel.size());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 直接缓冲区的方式完成文件的复制
     * 用open();方法获得Channel
     */
    @Test
    public void testCopyFileByChannelUserDirectBuffer() {
         String readPath = ChannelTest.class.getResource("/").getPath().substring(1) + "application.yaml";
         String writePath = ChannelTest.class.getResource("/").getPath().substring(1) + "application3.yaml";

         try(
                 FileChannel readChannel = FileChannel.open(Paths.get(readPath), StandardOpenOption.READ);
                 FileChannel writeChannel = FileChannel.open(
                    Paths.get(writePath),
                    StandardOpenOption.READ,
                    StandardOpenOption.WRITE,
                    StandardOpenOption.CREATE)
         ) {
            MappedByteBuffer readMappedByteBuffer = readChannel.map(FileChannel.MapMode.READ_ONLY, 0, readChannel.size());
            MappedByteBuffer writeMappedByteBuffer = writeChannel.map(FileChannel.MapMode.READ_WRITE, 0, readChannel.size());
            writeMappedByteBuffer.put(readMappedByteBuffer.get());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 非直接缓冲区的方式
     * 利用通道完成文件的复制
     * 这里面用到了jdk1.7的try-with-resource语法糖
     */
    @Test
    public void testCopyFileByChannel() {
        // 1：获取文件流
        //FileInputStream fileInputStream = new FileInputStream("application.yaml");
        //FileOutputStream fileOutputStream = new FileOutputStream("application2.yaml");

        // 2：通过文件流获取通道
        //FileChannel inChannel = fileInputStream.getChannel();
        //FileChannel outChannel = fileOutputStream.getChannel();

        try(
            FileInputStream fileInputStream = new FileInputStream(ChannelTest.class.getResource("/").getPath() + File.separator + "application.yaml");
            FileOutputStream fileOutputStream = new FileOutputStream(ChannelTest.class.getResource("/").getPath() + File.separator + "application2.yaml");
            FileChannel inChannel = fileInputStream.getChannel();
            FileChannel outChannel = fileOutputStream.getChannel()
        ) {
            // 3：分配一个指定大小的缓冲区
            ByteBuffer buffer = ByteBuffer.allocate(1024);

            // 4、通过缓冲区复制文件
            while(inChannel.read(buffer) != -1) {
                buffer.flip();
                outChannel.write(buffer);
                buffer.clear();
            }
        } catch (IOException e) {
            System.out.println(e);
        }

    }
}
